package com.luo.d3s.ext.aop.exception.aspect;

import com.luo.d3s.ext.aop.config.D3sAopConsts;
import com.luo.d3s.ext.aop.config.D3sAopProps;
import com.luo.d3s.ext.aop.exception.anno.D3sException;
import com.luo.d3s.ext.aop.exception.handler.D3sExceptionHandler;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;

/**
 * Service日志AOP增强切面
 *
 * @author luohq
 * @date 2023-01-29 09:57
 */
@Aspect
public class D3sExceptionAnnoHandlerAspect {

    private Logger log = LoggerFactory.getLogger(D3sExceptionAnnoHandlerAspect.class);

    public static final String EXCEPTION_POINTCUT = "(" + D3sAopConsts.EXCEPTION_ANNO_CLASS_POINTCUT
            + D3sAopConsts.SEPARATOR_OR + D3sAopConsts.EXCEPTION_ANNO_METHOD_POINTCUT + ") && "
            + D3sAopConsts.WEB_MVC_EXCLUDE_POINT;

    private D3sAopProps d3sAopProps;
    private D3sExceptionHandler d3sExceptionHandler;

    public D3sExceptionAnnoHandlerAspect(D3sAopProps d3sAopProps, D3sExceptionHandler d3sExceptionHandler) {
        this.d3sAopProps = d3sAopProps;
        this.d3sExceptionHandler = d3sExceptionHandler;
    }

    @PostConstruct
    void postConstruct() {
        log.info("D3s AOP EXT - @D3sException Annotation HANDLE - Started - Pointcut: {}, configProps: {}", EXCEPTION_POINTCUT, this.d3sAopProps.getException());
    }


    @Around(EXCEPTION_POINTCUT)
    public Object handleException(ProceedingJoinPoint point) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();
        Class methodClass = method.getDeclaringClass();
        //获取当前方法异常处理配置（优先获取方法上注解 -> 然后获取类上注解）
        D3sException d3sExceptionAnno = Optional.ofNullable(method.getAnnotation(D3sException.class))
                .orElse((D3sException) methodClass.getAnnotation(D3sException.class));
        //若方法、类上不存在注解 或者 若方法、类上注解设置了enable为false，则直接执行目标方法、忽略异常处理
        if (Objects.isNull(d3sExceptionAnno) || !d3sExceptionAnno.enabled()) {
            return point.proceed();
        }

        //异常处理
        try {
            return point.proceed();
        } catch (Throwable ex) {
            log.error("Handle Exception At {} - {}", this.convertMethodName(method, methodClass), String.valueOf(ex));
            return this.d3sExceptionHandler.handleException(method, ex);
        }
    }

    /**
     * 提取方法名称
     *
     * @param method      执行方法
     * @param methodClass 方法对应的类
     * @return 方法名称
     */
    protected String convertMethodName(Method method, Class methodClass) {
        return new StringJoiner(".")
                .add(methodClass.getSimpleName())
                .add(method.getName())
                .toString();
    }
}